﻿using System;
using System.Collections.Generic;
using System.Reflection;

using Microsoft.Practices.CompositeWeb.Interfaces;
using Microsoft.Practices.ObjectBuilder;

using WCSFContrib.Extensions.EventBroker;

namespace WCSFContrib.Extensions.CompositeWeb.ObjectBuilder.Strategies
{
	public class EventBrokerStrategy : BuilderStrategy
	{
		private static Dictionary<Type, List<MemberReference<EventInfo>>> publisingTypes;
		private static Dictionary<Type, List<MemberReference<MethodInfo>>> subscribingTypes;
		private static bool HasRunInit = false;

		/// <summary>
		/// Go through all code to find all pubs/subs.
		/// Validate them and enlist type-pubs/subs
		/// </summary>
		public static void ApplicationPubSubSanityCheck(IModuleEnumerator moduleEnumerator)
		{
			if (!HasRunInit)
			{
				IEnumerable<Type> types = Utility.Utils.GetApplicationTypes(moduleEnumerator);

				EventBrokerSanityCheck check = new EventBrokerSanityCheck();

				publisingTypes = check.GetPublishingHandlers(types);
				subscribingTypes = check.GetSubscribingHandlers(types);

				try
				{
					check.PerformSanityCheck(publisingTypes, subscribingTypes);
				}
				catch (NotSupportedException nse)
				{
					throw new ApplicationException("Application is insane!", nse);
				}
			}

			HasRunInit = true;
		}

		/// <summary>
		/// EventBrokerStrategy instances in the application share a global memory of publishingTypes and subscribingTypes. ApplicationPubSubSanityCheck must be called in App.Start
		/// </summary>
		/// <param name="context"></param>
		/// <param name="typeToBuild">The type we are building is the key in publisingTypes and subscribingTypes</param>
		/// <param name="existing">If type does publish/subscribe then existing is enlisted in the sessison EventBroker instance.</param>
		/// <param name="idToBuild"></param>
		public override object BuildUp(IBuilderContext context, Type typeToBuild, object existing, string idToBuild)
		{
			IRequestEventBroker eventBroker = RequestEventBroker.Instance;
			if (eventBroker != null)
			{
				List<MemberReference<EventInfo>> publisingTypeEvents;
				if (publisingTypes.TryGetValue(typeToBuild, out publisingTypeEvents))
					foreach (MemberReference<EventInfo> publisher in publisingTypeEvents)
					{
						eventBroker.AddPublisher(existing, publisher);
					}

				List<MemberReference<MethodInfo>> subscribingTypeMethods;
				if (subscribingTypes.TryGetValue(typeToBuild, out subscribingTypeMethods))
					foreach (MemberReference<MethodInfo> subscriber in subscribingTypeMethods)
					{
						eventBroker.AddSubscriber(existing, subscriber);
					}
			}
			return base.BuildUp(context, typeToBuild, existing, idToBuild);
		}
	}
}